package com.lifeonwalden.fincalc.fund.util;

import com.lifeonwalden.fincalc.bean.DataArrayPair;
import com.lifeonwalden.fincalc.bean.DataListPair;
import com.lifeonwalden.fincalc.bean.DateValuePair;
import com.lifeonwalden.fincalc.fund.bean.FundValueInfo;
import com.lifeonwalden.fincalc.fund.service.FundPerformanceProcessor;
import com.lifeonwalden.fincalc.utils.date.DatePeriodUtils;
import com.lifeonwalden.fincalc.utils.date.DateSeriesUtils;
import com.lifeonwalden.fincalc.utils.date.bean.PeriodAlignInfo;

import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;

public interface RelationshipCalcPreprocessor {
    /**
     * 预处理好增长数据用于相关性计算
     *
     * @param baseList
     * @param targetList
     * @param periodDays
     * @param baseConvertor
     * @param targetConvertor
     * @param <B>
     * @param <T>
     * @return
     */
    static <B, T> DataListPair processToList(List<B> baseList, List<T> targetList, int periodDays, Function<B, FundValueInfo> baseConvertor, Function<T, FundValueInfo> targetConvertor) {
        List<FundValueInfo> _baseList = baseList.stream().map(baseConvertor).collect(Collectors.toList());
        DateSeriesUtils.fillToNaturalDateSeries(_baseList, base -> base.getDate(), ((base, date) -> new FundValueInfo().setAccuNav(base.getAccuNav()).setDate(date).setNav(base.getNav())));

        PeriodAlignInfo alignInfo = DatePeriodUtils.locateAlign(_baseList, targetList, base -> base.getDate(), target -> targetConvertor.apply(target).getDate());
        List<FundValueInfo> _targetList = targetList.subList(alignInfo.getB().getStartIdx(), alignInfo.getB().getEndIndx() + 1).stream().map(targetConvertor).collect(Collectors.toList());
        DateSeriesUtils.fillToNaturalDateSeries(_targetList, target -> target.getDate(), ((target, date) -> new FundValueInfo().setAccuNav(target.getAccuNav()).setDate(date).setNav(target.getNav())));

        List<DateValuePair> baseChgList = FundPerformanceProcessor.calcPeriodChange(_baseList.subList(alignInfo.getA().getStartIdx(), alignInfo.getA().getEndIndx() + 1), (fundValueInfo -> fundValueInfo), periodDays).get();
        List<DateValuePair> targetChgList = FundPerformanceProcessor.calcPeriodChange(_targetList, (target -> target), periodDays).get();

        return new DataListPair().setA(baseChgList).setB(targetChgList);
    }

    /**
     * 预处理好增长数据用于相关性计算
     *
     * @param baseList
     * @param targetList
     * @param periodDays
     * @param baseConvertor
     * @param targetConvertor
     * @param <B>
     * @param <T>
     * @return
     */
    static <B, T> DataArrayPair process(List<B> baseList, List<T> targetList, int periodDays, Function<B, FundValueInfo> baseConvertor, Function<T, FundValueInfo> targetConvertor) {
        List<FundValueInfo> _baseList = baseList.stream().map(baseConvertor).collect(Collectors.toList());
        DateSeriesUtils.fillToNaturalDateSeries(_baseList, base -> base.getDate(), ((base, date) -> new FundValueInfo().setAccuNav(base.getAccuNav()).setDate(date).setNav(base.getNav())));

        PeriodAlignInfo alignInfo = DatePeriodUtils.locateAlign(_baseList, targetList, base -> base.getDate(), target -> targetConvertor.apply(target).getDate());
        List<FundValueInfo> _targetList = targetList.subList(alignInfo.getB().getStartIdx(), alignInfo.getB().getEndIndx() + 1).stream().map(targetConvertor).collect(Collectors.toList());
        DateSeriesUtils.fillToNaturalDateSeries(_targetList, target -> target.getDate(), ((target, date) -> new FundValueInfo().setAccuNav(target.getAccuNav()).setDate(date).setNav(target.getNav())));

        List<DateValuePair> baseChgList = FundPerformanceProcessor.calcPeriodChange(_baseList.subList(alignInfo.getA().getStartIdx(), alignInfo.getA().getEndIndx() + 1), (fundValueInfo -> fundValueInfo), periodDays).get();
        List<DateValuePair> targetChgList = FundPerformanceProcessor.calcPeriodChange(_targetList, (target -> target), periodDays).get();
        double[] baseDatas = new double[baseChgList.size()];
        double[] targetDatas = new double[targetChgList.size()];
        for (int i = 0; i < baseChgList.size(); i++) {
            baseDatas[i] = baseChgList.get(i).getValue().doubleValue();
            targetDatas[i] = targetChgList.get(i).getValue().doubleValue();
        }

        return new DataArrayPair().setA(baseDatas).setB(targetDatas);
    }
}
